# -*- coding: utf-8 -*-
"""
    @Author ljy
    @Date 2020/11/4 17:26
    @Describe 
"""
from starlette.requests import Request

from fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from fastapi.exceptions import RequestValidationError
from starlette.middleware.cors import CORSMiddleware
from starlette.responses import JSONResponse, HTMLResponse, Response, StreamingResponse
from starlette.staticfiles import StaticFiles
import settings
from errors import FileMaxSizeError, JPGError

app = FastAPI(title='爱读书App接口服务',
              bersion = '1.0',
              description='面向app,提供数据的API接口，包括会员模块、搜索、分类、活动、积分、评论、我的书架、广告')



app.mount('/s',StaticFiles(directory=settings.STATIC_DIR),name='static')

# 添加跨域请求中间件
app.add_middleware(CORSMiddleware,
                   allow_origins='*',  # ACCESS_CONTROL_ALLOW_ORIDINS
                   allow_credentials=True,   # ACCESS_CONTROL_ALLOW_CREDENTIALS
                   allow_methods=["*"],  # ACCESS_CONTROL_ALLOW_METHODS
                   allow_headers=["*"],    # ACCESS_CONTROL_ALLOW
                  )

# 不用验证权限的path接口
pass_paths = [
    '/docs',
    '/openapi.json',
    '/login',
    '/regist'
]

# 定义中间件（实现日志收集）
@app.middleware('http')
async def logger_handler(request:Request,next_call):
    # 请求处理之前，实现权限验证，数据缓存和节流（限制IP的1秒内的请求次数以及）以及（ELK）
    # 请求处理之前 提取请求路径中path,如http://localhost:8000 /login,提取是/login
    path = str(request.url)[len(str(request.base_url))-1:]  # request的url和base_url --> URL类
    # 清除path路径中的查询参数，/login?username=&password=
    path = path[:path.index('?')] if '?' in path else path
    # 放开请求路径，不拦截
    if path in pass_paths:
        return await next_call(request)

    headers = request.headers # 从请求头中验证是否存在token
    # 尝试获取json数据，建议不要获取数据
    try:
        json = await request.json()  # 上传的json数据，可能是不存在的
        print('---->request',json)
    except:
        pass
    # query:QueryParams
    # 尝试获取查询参数和路径参数
    args = {'path_args':request.path_params,'query_args':request.query_params}
    print('---->request',path,headers,json,args,request.client.host)

    print(request.auth)

    resp:StreamingResponse = await next_call(request)
    # 如果成功响应时，则记录本次操作
    print('---->response:',resp.status_code,resp.charset)
    return resp

# 自定义异常处理
@app.exception_handler(Exception)
async def handler_custom_error(request:Request,error):
    if isinstance(error,JPGError):
        return JSONResponse({'msg':'图片不是jpg格式'},400)
    elif isinstance(error,FileMaxSizeError):
        resp = JSONResponse({'msg':'图片大小不能超过2M'},401,headers={'x-error':'size out of 2M'})
        return resp



# 自定义处理请求数据验证的错误
@app.exception_handler(RequestValidationError)
async def handler_validation_error(req,e:RequestValidationError):
    print([a for a in dir(e) if not a.startswith('__')])
    errors = {
        'value_error.missing':'必填项',
        'type_error.integer':'字段内容必须是数值',
        'value_error.str.regex':'字段数据不合法,{}',
        'value_error.number.not_ge':'数值太低,{}',
        'value_error.number.not_gt':'数值太低:,{}',
        'value_error.number.not_le':'数值太高:{}',
        'value_error.number.not_lt':'数值太高:{}',
        'value_error.any_str.min_length':'字符串长度不足:{}',
        'value_error.any_str.max_length':'字符串超出最大长度：{}'
    }
    resp_error = {
        '.'.join(item['loc']):errors[item['type']].format(list(item['ctx'].values())[0]) if 'ctx' in item else errors[item['type']]
        for item in e.errors()
    }
    return JSONResponse(jsonable_encoder({'errors':resp_error}),status_code=400)


@app.exception_handler(404)
async def handle_not_found(request:Request,error):
    html = f"""
        <h1 style='color:red'>请求的资源不存在，需要开发则联系管理员</h1>
        <hr>
        <h3>当前请求地址：{request.url}</h3>
    """
    return HTMLResponse(html,status_code=404)


@app.exception_handler(500)
async def handle_server_error(request:Request,error):
    html = f"""
        <h1 style='color:red'>出bug了，请联系管理员</h1>
        <hr>
        <h3>{error}</h3>
    """
    return HTMLResponse(html,status_code=500)

